<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<style type=text/css>body{font-family:monospace;}</style>
	<title>Android编译优化之dex2oat</title>
	<meta name="description" content="Jin&#39;s Blog">
	
	<link rel="stylesheet" href="/css/style.css">
	
	
</head>
<body>
	<header>
	================<br>
	== <a href="https://acidsweet.gitee.io">Jin&#39;s Blog</a> ==<br>
	================
	<div style="float: right;"></div><br>
	<p>
	<nav>
			<a href="/"><b>Start</b></a>.
			
			
			<a href="/post/"><b>Archives</b></a>.
			
			<a href="/tags/"><b>Tags</b></a>.
			
			<a href="/categories/"><b>Categories</b></a>.
			
			<a href="/index.xml"><b>Rss</b></a>.
			
	</nav>
	</p>
	
</header>

	
	<main>
		<article>
			<h1>Android编译优化之dex2oat</h1>
			<b><time>23.02.2021 17:24</time></b>
		       

			<div>
				<h1 id="1虚拟机">1、虚拟机</h1>
<h2 id="11android虚拟机编年史">1.1、Android虚拟机编年史</h2>
<p><img src="https://i.loli.net/2021/09/22/cB5tgGPTXbKfdrE.png" alt="img" style="zoom:50%;" /></p>
<p>Android虚拟机主要有两种：Davlik和Art，Art在Android L后正式成为了默认的虚拟机；有点年岁的用户可能经历过可以在开发者选项切换虚拟机的时代🐶</p>
<blockquote>
<p>JIT vs AOT</p>
<p><code>JIT(just in time)</code>:在运行时编译，边运行边编译。每启动一个应用程序，都会相应地启动一个 dalvik 虚拟机，启动时会建立JIT 线程，一直在后台运行。当某段代码被调用时，虚拟机会判断它是否需要编译成机器码，如果需要，就做一个标记，JIT 线程不断判断此标记，如果发现被设定就把它编译成机器码，并将其机器码地址及相关信息放入 entry table 中，下次执行到此就跳到机器码段执行，而不再解释执行，从而提高速度。【运行时优化】</p>
<p><img src="https://i.loli.net/2021/09/22/tg1EbOqh9kupw4z.png" alt="img" style="zoom:50%;" /></p>
<p><code>AOT(ahead of time)</code>:在APK安装的时候就会做预先编译动作，编译好的文件是OAT文件，该文件本质上是一个ELF文件，它是可以直接被执行的文件。【编译时优化】</p>
<p><img src="https://i.loli.net/2021/09/22/RtFDox2mZ9M7q3e.png" alt="img" style="zoom:50%;" /></p>
<p><code>混合运行时</code>:Android N后Google探索了一个结合JIT和AOT的混合策略：</p>
<ul>
<li>应用在安装时不做编译，而是解释字节码，所以可以快速启动</li>
<li>JIT的数据被分析并保存(profile文件)，在设备空闲、充电时进行AOT编译</li>
<li><img src="https://i.loli.net/2021/09/22/SuWGpHFCZirlqXv.png" alt="img" style="zoom:50%;" /></li>
</ul>
</blockquote>
<h2 id="12虚拟机和字节码">1.2、虚拟机和字节码</h2>
<p><img src="https://i.loli.net/2021/09/22/ujVlfqCAP5nzxFG.png" alt="img" style="zoom:33%;" /></p>
<ul>
<li>传统的jvm：.java&ndash;[javac]&ndash;&gt;.class</li>
<li>Android虚拟机：.java&ndash;[javac]&ndash;&gt;.class&ndash;[dx]&ndash;&gt;.dex</li>
</ul>
<blockquote>
<p>Dalvik vs Art
<img src="https://i.loli.net/2021/09/22/zHXCDYqudRphIy8.png" alt="img" style="zoom:50%;" /></p>
</blockquote>
<h1 id="2dexodex以及vdex">2、dex、odex以及vdex</h1>
<h2 id="21dex文件">2.1、dex文件</h2>
<p><img src="https://i.loli.net/2021/09/22/SY65TRICg1xkEGW.png" alt="img" style="zoom:33%;" /></p>
<p><img src="https://i.loli.net/2021/09/22/VbR286ywtoZfnu7.png" alt="img" style="zoom: 50%;" /></p>
<blockquote>
<p><a href="https://blog.csdn.net/dashuniuniu/article/details/50347149">基于栈的虚拟机 VS 基于寄存器的虚拟机</a></p>
</blockquote>
<h2 id="22odexoptimized-dex文件">2.2、odex(optimized dex)文件</h2>
<p><code>Dalvik VM</code>:apk文件的本质是zip包，字节码分布在其中的classes*.dex内，为了优化每次unzip和一些依赖的耗时，Dalvik下会通过<code>dexopt</code>将apk中的dex抽出，优化成odex文件，存在<code>/data/dalvik-cache/</code>下(并且会删除apk内dex)；这样带来三个主要好处：</p>
<ul>
<li>启动时不需要解压apk获取dex文件了 → 启动耗时优化</li>
<li>同一时刻一个应用不会存在两份dex了 → 内存占用优化</li>
<li>apk不包括dex了，提取或者反编译难度变大 → 安全优化</li>
</ul>
<p><code>Art VM</code>:在Art虚拟机下，odex文件本质是oat文件，也就是可以被linux底层直接运行的可执行文件。Art下会通过dex2oat将apk中的dex优化成odex(oat文件)。</p>
<blockquote>
<p>ELF&amp;OAT</p>
<p><code>ELF(Executable and Linking Format)</code>是一种对象文件的格式，用于定义不同类型的对象文件(Object files)中都放了什么东西、以及都以什么样的格式去放这些东西。它自最早在 System V系统上出现后，被 xNIX 世界所广泛接受，作为缺省的二进制文件格式来使用。可以说，ELF 是构成众多 xNIX 系统的基础之一。</p>
<p><code>OAT文件</code>是一种Android私有ELF文件格式，它不仅包含有从DEX文件翻译而来的本地机器指令，还包含有原来的DEX文件内容。APK在安装的过程中，会通过dex2oat工具生成一个OAT文件。对于apk来说，oat文件实际上就是对odex文件的包装，即oat=odex，而对于一些framework中的一些jar包，会生成相应的oat尾缀的文件</p>
</blockquote>
<h2 id="22vdexverified-dex">2.2、vdex(verified dex)</h2>
<p>Android O(8.0)后，为了降低dex2oat的耗时、避免不必要的验证Dex文件合法性的过程，Android引入了vdex机制。</p>
<ul>
<li>Vdex = dex + quicken info</li>
</ul>
<p>而这时候dex2oat再也不是直接从dex生成odex了，而是从vdex中抽取文件生成odex</p>
<ul>
<li>全部源码 = vdex + odex</li>
</ul>
<h1 id="3dex2oat">3、dex2oat</h1>
<p>上面两个章节我们回顾Android虚拟机的一些事情，知道了在目前主流的Art虚拟机下通过AOT技术会将vdex转换成odex文件，这样可以有效的提高应用的执行效率。而这个转化过程正是通过系统的dex2oat工具完成的，这个章节会来了解一下dex2oat相关的知识。</p>
<h2 id="31触发时机">3.1、触发时机</h2>
<p><img src="https://i.loli.net/2021/09/22/1FE4Uldjk7fMe9h.png" alt="img" style="zoom:33%;" /></p>
<h2 id="32编译级别">3.2、编译级别</h2>
<p>
<figure>
  <img src="https://i.loli.net/2021/09/22/a4o3MrGgnKiBywT.png" alt="img" />
</figure>


</p>
<p>
<figure>
  <img src="https://i.loli.net/2021/09/22/WQC2hwP9k84MyHb.png" alt="img" />
</figure>


</p>
<p>我们知道vdex+odex=全部代码，当odex越大，vdex越小时，我们优化编译的效果也越好；所以上面的编译级别也会遵循占用空间越大，编译优化效果越好的原则，主要几个级别编译效果：</p>
<p><strong>verify&lt;space&lt;speed&lt;everything</strong></p>
<p>在Android N后由于混合模式的采用，多出了几个**-profile的模式；这里主要涉及到profile文件的生成，篇幅原因可以参考<a href="https://blog.csdn.net/hl09083253cy/article/details/78418809">Android profile-guided dex2oat_大将军王虎剩的专栏</a>中的profile-guide内容。</p>
<blockquote>
<p>profile文件：/data/misc/profiles/cur/0/com.***.home/primary.prof</p>
<p>每个app的profile文件都在 /data/misc/profiles/ 目录下。profile文件用来记录运行比较频繁的代码，用来进行 profile-guide 编译，使得 dex2oat编译代码更精准。</p>
<p>profile的创建</p>
<p>App安装的过程中，会调用到 isntalld的 create_app_data()函数，如果当前支持profile编译，则会为app创建 profile文件。</p>
</blockquote>
<blockquote>
<p>查看系统定义的几个场景的编译级别</p>
</blockquote>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-fallback" data-lang="fallback">→ adb shell getprop

...

[pm.dexopt.ab-ota]: [speed-profile]

[pm.dexopt.bg-dexopt]: [speed-profile]

[pm.dexopt.boot]: [verify]

[pm.dexopt.first-boot]: [quicken]

[pm.dexopt.inactive]: [verify]

[pm.dexopt.install]: [speed-profile]

[pm.dexopt.shared]: [speed]
</code></pre></td></tr></table>
</div>
</div><p>REF:</p>
<p>[1] <a href="https://www.jianshu.com/p/bcc4a9209ef5">Android虚拟机的JIT编译器</a></p>
<p>[2] <a href="https://www.jianshu.com/p/389911e2cdfb">Dalvik,ART与ODEX相爱相生</a></p>
<p>[3] <a href="https://blog.csdn.net/TaylorPotter/article/details/89855346">Android dex，odex，oat，vdex，art文件结构学习总结</a></p>

			</div>
		</article>
	</main>
<aside>
	<div>
		<div>
			<h3>LATEST POSTS</h3>
		</div>
		<div>
			<ul>
				
				<li><a href="/post/fundamental_analysis_primer/">基本面分析基础</a></li>
				
				<li><a href="/post/read_from_zhangkun/">读&lt;对话易方达张坤&gt;</a></li>
				
				<li><a href="/post/practice_in_aip/">基金定投量化实战</a></li>
				
				<li><a href="/post/dex2oat_in_android/">Android编译优化之dex2oat</a></li>
				
				<li><a href="/post/poker_simple_guide_book/">德州扑克入门简易教程</a></li>
				
			</ul>
		</div>
	</div>
</aside>


	<footer>
	<p>&copy; 2022 <a href="https://acidsweet.gitee.io"><b>author by jin.shang</b></a>.
	</p>
</footer>

</body>
</html>
